package com.friend.service.user.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.common.core.constants.CacheConstants;
import com.common.core.constants.Constants;
import com.common.core.constants.HttpConstants;
import com.common.core.domain.LoginUser;
import com.common.core.domain.Result;
import com.common.core.domain.vo.LoginUserVO;
import com.common.core.enums.ResultCode;
import com.common.core.enums.UserIdentity;
import com.common.core.enums.UserStatus;
import com.common.core.utils.ThreadLocalUtil;
import com.common.message.service.AliSmsService;
import com.common.redis.service.RedisService;
import com.common.security.exception.ServiceException;
import com.common.security.service.TokenService;
import com.friend.domain.user.User;
import com.friend.domain.user.dto.UserDTO;
import com.friend.domain.user.dto.UserUpdateDTO;
import com.friend.domain.user.vo.UserVO;
import com.friend.manager.UserCacheManager;
import com.friend.mapper.user.UserMapper;
import com.friend.service.user.UserService;
import jakarta.annotation.Resource;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Value;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
@RefreshScope
public class UserServiceImpl implements UserService {
    @Resource
    UserMapper userMapper;

    @Resource
    AliSmsService aliSmsService;

    @Resource
    TokenService tokenService;

    @Resource
    RedisService redisService;

    @Resource
    UserCacheManager userCacheManager;

    @Value("${jwt.secret}")
    private String secret;

    @Value("${sms.code-expiration:5}")
    private Long phoneCodeExpiration;   //验证码存在时间，单位分钟

    @Value("${sms.send-limit:3}")
    private Integer sendLimit;      //最大请求次数

    @Value("${file.oss.downloadUrl}")
    private String downloadUrl;

    private final boolean fg=true;     //fg为true表示测试环境，不真正发送短信

    @Override
    public boolean sendCode(UserDTO userDTO) {
        if(!checkPhone(userDTO.getPhone())){
            //手机号不合法
            throw new ServiceException(ResultCode.FAILED_USER_PHONE);
        }
        String phoneCodeKey = getPhoneCodeKey(userDTO.getPhone());

        //获得剩余存储时间
        Long expire = redisService.getExpire(phoneCodeKey, TimeUnit.SECONDS);
        if(expire!=null && (phoneCodeExpiration*60-expire)<60){
            //防止有人刷信息，设置60s只能发送一次
            throw new ServiceException(ResultCode.FAILED_FREQUENT);
        }

        //设置每日发送次数，每个手机号每日只能发送50次短信
        String codeTimeKey = getCodeTimeKey(phoneCodeKey);
        Long sendTimes = redisService.getCacheObject(codeTimeKey, Long.class);
        if(sendTimes!=null && sendTimes>=sendLimit){
            throw new ServiceException(ResultCode.FAILED_TIME_LIMIT);
        }

        //生成验证码，发送验证码
        String code= fg ? "123456" : RandomUtil.randomNumbers(6);
        //将验证码存入Redis中
        redisService.setCacheObject(phoneCodeKey,code,phoneCodeExpiration, TimeUnit.MINUTES);
        if(!fg){
            boolean sendMobileCode = aliSmsService.sendMobileCode(userDTO.getPhone(), code);
            if(!sendMobileCode){
                //验证码发送失败
                throw new ServiceException(ResultCode.FAILED_SEND_CODE);
            }
        }
        redisService.increment(codeTimeKey);
        if (sendTimes == null) {
            //说明是当天第一次发起获取验证码的请求
            //发送次数每天刷新
            long seconds = ChronoUnit.SECONDS.between(LocalDateTime.now(),
                    LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0));
            redisService.expire(codeTimeKey, seconds, TimeUnit.SECONDS);
        }
        return true;
    }

    @Override
    public String codeLogin(String phone, String code) {
        //先验证  验证码是否正确
        checkCode(phone,code);
        //再从数据库中获取用户信息
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone, phone));
        if(user==null){
            //说明是新用户,需完成注册功能
            user = new User();
            user.setPhone(phone);
            user.setStatus(UserStatus.Normal.getValue());
            userMapper.insert(user);
        }
        return tokenService.createToken(user.getUserId(), secret,
                UserIdentity.ORDINARY.getValue(),user.getNickName(),user.getHeadImage());
    }

    @Override
    public boolean logout(String token) {
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        return tokenService.deleteLoginUser(token,secret);
    }

    @Override
    public Result<LoginUserVO> info(String token) {
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }

        LoginUser loginUser = tokenService.getLoginUser(token, secret);

        if(loginUser==null){
            return Result.fail();
        }

        LoginUserVO loginUserVO=new LoginUserVO();
        loginUserVO.setNickName(loginUser.getNickName());
        if(StrUtil.isNotEmpty(loginUser.getHeadImage())){
            loginUserVO.setHeadImage(downloadUrl+loginUser.getHeadImage());
        }

        return Result.success(loginUserVO);
    }

    @Override
    public Result<UserVO> detail() {
        //从ThreadLocal中获取用户Id
        Long userId= ThreadLocalUtil.get(Constants.USER_ID,Long.class);
        if(userId==null){
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        //先从Redis中读取，看看用户是否存在
        UserVO userVO=userCacheManager.getUserById(userId);
        if(userVO==null){
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        if(StrUtil.isNotEmpty(userVO.getHeadImage())){
            userVO.setHeadImage(downloadUrl+userVO.getHeadImage());
        }
        return Result.success(userVO);
    }

    @Override
    public int edit(UserUpdateDTO userUpdateDTO) {
        //从ThreadLocal中获取用户Id
        Long userId= ThreadLocalUtil.get(Constants.USER_ID,Long.class);
        if(userId==null){
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        //从数据库获取用户信息
        User user=userMapper.selectById(userId);
        if(user==null){
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        //更新数据
        user.setNickName(userUpdateDTO.getNickName());
        user.setSex(userUpdateDTO.getSex());
        user.setSchoolName(userUpdateDTO.getSchoolName());
        user.setMajorName(userUpdateDTO.getMajorName());
        user.setPhone(userUpdateDTO.getPhone());
        user.setEmail(userUpdateDTO.getEmail());
        user.setWechat(userUpdateDTO.getWechat());
        user.setIntroduce(userUpdateDTO.getIntroduce());
        //先更新数据库
        int insert = userMapper.updateById(user);
        //再更新缓存中的数据
        userCacheManager.refreshUser(user);
        //从ThreadLocal中获取用户的key
        String userKey=ThreadLocalUtil.get(Constants.USER_KEY, String.class);
        //更新Redis中的token
        tokenService.refreshLoginUser(user.getNickName(),user.getHeadImage(),userKey);
        return insert;
    }

    @Override
    public int updateHeadImage(String headImage) {

        //从ThreadLocal中获取用户Id
        Long userId= ThreadLocalUtil.get(Constants.USER_ID,Long.class);
        if(userId==null){
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        //从数据库获取用户信息
        User user=userMapper.selectById(userId);
        if(user==null){
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        //更新数据
        user.setHeadImage(headImage);
        //先更新数据库
        int insert = userMapper.updateById(user);
        //再更新缓存中的数据
        userCacheManager.refreshUser(user);
        //从ThreadLocal中获取用户的key
        String userKey=ThreadLocalUtil.get(Constants.USER_KEY, String.class);
        //更新Redis中的token
        tokenService.refreshLoginUser(user.getNickName(),user.getHeadImage(),userKey);
        return insert;
    }

    //验证手机号码是否合法
    public static boolean checkPhone(String phone) {
        Pattern regex = Pattern.compile("^1[2|3|4|5|6|7|8|9][0-9]\\d{8}$");
        Matcher m = regex.matcher(phone);
        return m.matches();
    }

    //验证 验证码是否正确
    private void checkCode(String phone,String code){
        String phoneCodeKey = getPhoneCodeKey(phone);
        //从Redis中获取验证码
        String cacheCode = redisService.getCacheObject(phoneCodeKey, String.class);
        if(StrUtil.isEmpty(cacheCode)){
            //验证码为空
            throw new ServiceException(ResultCode.FAILED_INVALID_CODE);
        }
        if(!cacheCode.equals(code)){
            throw new ServiceException(ResultCode.FAILED_ERROR_CODE);
        }
        //验证正确,删除Redis中的验证码
        redisService.deleteObject(phoneCodeKey);
    }


    private String getPhoneCodeKey(String phone){
        return CacheConstants.PHONE_CODE_KEY+phone;
    }

    private String getCodeTimeKey(String phone){
        return CacheConstants.CODE_TIME_KEY+phone;
    }
}
